package com.duck.duckchatbackend.websocket;

import com.alibaba.fastjson.JSON;
import com.duck.duckchatbackend.entity.constans.Constants;
import com.duck.duckchatbackend.entity.dto.MessageSendDto;
import com.duck.duckchatbackend.entity.dto.WsInitData;
import com.duck.duckchatbackend.entity.enums.MessageTypeEnum;
import com.duck.duckchatbackend.entity.enums.UserContactApplyStatusEnum;
import com.duck.duckchatbackend.entity.enums.UserContactTypeEnum;
import com.duck.duckchatbackend.entity.po.*;
import com.duck.duckchatbackend.entity.query.ChatMessageQuery;
import com.duck.duckchatbackend.entity.query.ChatSessionUserQuery;
import com.duck.duckchatbackend.entity.query.UserContactApplyQuery;
import com.duck.duckchatbackend.entity.query.UserInfoQuery;
import com.duck.duckchatbackend.mappers.ChatSessionUserMapper;
import com.duck.duckchatbackend.mappers.UserContactApplyMapper;
import com.duck.duckchatbackend.mappers.UserInfoMapper;
import com.duck.duckchatbackend.redis.RedisComponent;
import com.duck.duckchatbackend.service.ChatMessageService;
import com.duck.duckchatbackend.service.ChatSessionUserService;
import com.duck.duckchatbackend.service.UserContactApplyService;
import com.duck.duckchatbackend.service.UserContactService;
import com.duck.duckchatbackend.utils.JsonUtils;
import com.duck.duckchatbackend.utils.StringTools;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.duck.duckchatbackend.entity.constans.Constants.MILLISECOND_3DAYS_AGO;

/**
 * ChannelContextUtils类用于处理和管理Channel与用户ID之间的映射关系
 * 它提供了一种方式来添加或更新Channel上的用户ID上下文信息
 */
@Component
public class ChannelContextUtils {
    private static final Logger logger = LoggerFactory.getLogger(ChannelContextUtils.class);
    private static final ConcurrentHashMap<String, Channel> USER_CONTEXT_MAP = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<String, ChannelGroup> GROUP_CONTEXT_MAP = new ConcurrentHashMap<>();


    @Resource
    private ChatSessionUserService chatSessionUserService;

    @Resource
    private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

    @Resource
    private RedisComponent redisComponent;

    @Resource
    private ChatMessageService chatMessageService;

    @Resource
    private UserContactApplyMapper<UserContactApply, UserContactApplyQuery> userContactApplyMapper;

    /**
     * 向Channel中添加用户ID上下文信息
     * 如果Channel上不存在对应的用户ID信息，则创建并添加新的用户ID信息
     *
     * @param userId  用户ID，作为要添加到Channel上下文中的值
     * @param channel Channel对象，代表与用户建立的网络连接
     */
    public void addContext(String userId, Channel channel) {
        // 获取Channel的唯一标识符并转换为字符串形式
        String channelId = channel.id().toString();
        logger.info("channelId:{}", channelId);
        AttributeKey attributeKey = null;
        // 检查是否已存在对应ChannelId的AttributeKey
        if (!AttributeKey.exists(channelId)) {
            // 如果不存在，则创建一个新的AttributeKey
            attributeKey = AttributeKey.newInstance(channelId);
        } else {
            // 如果存在，则获取对应的AttributeKey
            attributeKey = AttributeKey.valueOf(channelId);
        }
        // 使用AttributeKey将userId关联到Channel上
        channel.attr(attributeKey).set(userId);
        List<String> contactIdList = redisComponent.getUserContactList(userId);
        for (String groupId : contactIdList) {
            if (groupId.startsWith(UserContactTypeEnum.GROUP.getPrefix())) {
                add2Group(groupId, channel);
            }
        }

        USER_CONTEXT_MAP.put(userId, channel);
        redisComponent.saveUserHeartBeat(userId);
        //更新用户最后连接时间
        UserInfo updateInfo = new UserInfo();
        updateInfo.setLastLoginTime(new Date());
        userInfoMapper.updateByUserId(updateInfo, userId);

        //给用户发送消息，只发三天之内产生的消息。
        UserInfo userInfo = userInfoMapper.selectByUserId(userId);
        Long sourceLastOffTime = userInfo.getLastOffTime();
        //这里避免毫秒时间差，所以减去1秒的时间
        //如果时间太久，只取最近三天的消息数
        Long lastOffTime = sourceLastOffTime;
        if (sourceLastOffTime != null && System.currentTimeMillis() - Constants.MILLISECOND_3DAYS_AGO > sourceLastOffTime) {
            lastOffTime = Constants.MILLISECOND_3DAYS_AGO;
        }
        /*
         * 1.查询会话信息 查询用户所有的会话消息，保证了设备会话同步
         * */
        ChatSessionUserQuery sessionUserQuery = new ChatSessionUserQuery();
        sessionUserQuery.setUserId(userId);
        sessionUserQuery.setOrderBy("last_receive_time desc");
        List<ChatSessionUser> chatSessionUserList = chatSessionUserService.findListByParam(sessionUserQuery);

        WsInitData wsInitData = new WsInitData();
        wsInitData.setChatSessionList(chatSessionUserList);

        /*
         * 2.查询聊天信息 查询用户三天内产生的消息，保证了设备消息同步
         * */
        //查询所有联系人
        List<String> groupIdList = contactIdList.stream().filter(item -> item.startsWith(UserContactTypeEnum.GROUP.getPrefix())).collect(Collectors.toList());
        groupIdList.add(userId);
        ChatMessageQuery messageQuery = new ChatMessageQuery();

        messageQuery.setContactIdList(groupIdList);
        messageQuery.setLastReceiveTime(lastOffTime);
        List<ChatMessage> chatMessageList = chatMessageService.findListByParam(messageQuery);

        wsInitData.setChatMessageList(chatMessageList);

        /*
         * 3. 查询好友申请
         * */
        UserContactApplyQuery applyQuery = new UserContactApplyQuery();
        applyQuery.setReceiveUserId(userId);
        applyQuery.setLastApplyTimestamp(lastOffTime);
        applyQuery.setStatus(UserContactApplyStatusEnum.INIT.getStatus());
        Integer applyCount = userContactApplyMapper.selectCount(applyQuery);
        wsInitData.setApplyCount(applyCount);

        //发送消息
        MessageSendDto messageSendDto = new MessageSendDto();
        messageSendDto.setMessageType(MessageTypeEnum.INIT.getType());
        messageSendDto.setContactId(userId);
        messageSendDto.setExtendData(wsInitData);

        sendMsg(messageSendDto, userId);
    }




    // 向指定组中添加通道
    public void add2Group(String groupId, Channel channel) {
        // 获取指定组的通道组
        ChannelGroup group = GROUP_CONTEXT_MAP.get(groupId);
        // 如果组不存在，则创建一个新的通道组
        if (group == null) {
            group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
            // 将新的通道组放入GROUP_CONTEXT_MAP中
            GROUP_CONTEXT_MAP.put(groupId, group);
        }
        // 如果通道为空，则直接返回
        if (channel == null) {
            return;
        }
        // 将通道添加到通道组中
        group.add(channel);
    }

    public void removeContext(Channel channel) {
        //获取channel的Attribute
        Attribute<String> attribute = channel.attr(AttributeKey.valueOf(channel.id().toString()));
        //获取Attribute中的userId
        String userId = attribute.get();
        //如果userId为空，则从USER_CONTEXT_MAP中移除
        if (!StringTools.isEmpty(userId)) {
            USER_CONTEXT_MAP.remove(userId);
        }
        //从redis中移除用户的心跳
        redisComponent.removeUserHeartBeat(userId);
        //更新用户最后离线时间
        UserInfo userInfo = new UserInfo();
        userInfo.setLastOffTime(System.currentTimeMillis());
        userInfoMapper.updateByUserId(userInfo, userId);
    }

    // 根据传入的MessageSendDto对象，发送消息
   public void sendMessage(MessageSendDto messageSendDto) {
        // 根据contactId获取用户联系类型
        UserContactTypeEnum contactTypeEnum = UserContactTypeEnum.getByPrefix(messageSendDto.getContactId());
        // 根据用户联系类型，发送消息
        switch (contactTypeEnum) {
            case USER:
                // 发送消息给用户
                send2User(messageSendDto);
                break;
            case GROUP:
                // 发送消息给群组
                sendMsg2Group(messageSendDto);
        }
    }
    public void closeContext(String userId) {
        if (StringTools.isEmpty(userId)) {
            return;
        }
        redisComponent.cleanUserTokenByUserId(userId);   //清除缓存的user信息
        Channel channel = USER_CONTEXT_MAP.get(userId);
        USER_CONTEXT_MAP.remove(userId);    //移除channel
        if (channel != null) {//关闭通道
            channel.close();
        }
    }

    /**
     * 发送消息给用户
     */
    private void send2User(MessageSendDto messageSendDto) {
        String contactId = messageSendDto.getContactId();
        sendMsg(messageSendDto, contactId);
        //强制下线
        if (MessageTypeEnum.FORCE_OFF_LINE.getType().equals(messageSendDto.getMessageType())) {
            closeContext(contactId);
        }
    }
    /**
     * 发送消息到组
     */
    private void sendMsg2Group(MessageSendDto messageSendDto) {
        if (messageSendDto.getContactId() == null) {
            return;
        }

        ChannelGroup group = GROUP_CONTEXT_MAP.get(messageSendDto.getContactId());
        if (group == null) {
            return;
        }
        //向通道组发送消息
        group.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(messageSendDto)));

        //移除群聊
        MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByType(messageSendDto.getMessageType());
        if (MessageTypeEnum.LEAVE_GROUP == messageTypeEnum || MessageTypeEnum.REMOVE_GROUP == messageTypeEnum) {
            String userId = (String) messageSendDto.getExtendData();
            redisComponent.removeUserContact(userId, messageSendDto.getContactId());
            Channel channel = USER_CONTEXT_MAP.get(userId);
            if (channel == null) {
                return;
            }
            group.remove(channel);
        }

        if (MessageTypeEnum.DISSOLUTION_GROUP == messageTypeEnum) {
            GROUP_CONTEXT_MAP.remove(messageSendDto.getContactId());
            group.close();
        }
    }


    //发送消息
    public  void sendMsg(MessageSendDto messageSendDto, String receiveId) {
        // 如果接收者id为空，则返回
        if (receiveId == null) {
            return;
        }
        // 获取接收者的channel
        Channel sendchannel = USER_CONTEXT_MAP.get(receiveId);
        // 如果接收者的channel为空，则返回
        if (sendchannel == null) {
            return;
        }
        //相对于客户端而言，联系人就是发送人
        // 设置消息的联系人id和联系人名称
        messageSendDto.setContactId(messageSendDto.getSendUserId());
        messageSendDto.setContactName(messageSendDto.getSendUserNickName());
        // 将消息转换为json格式，并通过channel发送
        sendchannel.writeAndFlush(new TextWebSocketFrame(JsonUtils.convertObj2Json(messageSendDto)));
    }
}






